package com.heaven1949.tmall.common.web.exception;

import com.heaven1949.tmall.common.core.dto.ApiResult;
import com.heaven1949.tmall.common.core.dto.ResultCode;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.support.DefaultMessageSourceResolvable;
import org.springframework.http.HttpStatus;
import org.springframework.http.converter.HttpMessageNotReadableException;
import org.springframework.validation.BindException;
import org.springframework.validation.BindingResult;
import org.springframework.web.HttpRequestMethodNotSupportedException;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.MissingServletRequestParameterException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import org.springframework.web.multipart.MaxUploadSizeExceededException;
import org.springframework.web.multipart.support.MissingServletRequestPartException;

import javax.servlet.http.HttpServletRequest;
import javax.validation.ConstraintViolation;
import javax.validation.ConstraintViolationException;
import java.util.Objects;
import java.util.Set;
import java.util.stream.Collectors;

/**
 * 全局异常处理
 *
 * @author zhangjian
 * @date 2023-03-09 13:35:08
 */
@Slf4j
@RestControllerAdvice
public class GlobalExceptionHandler {

    /**
     * 捕获ValidException异常
     *
     * @param request
     * @param exception
     * @return
     */
    @ExceptionHandler(ValidException.class)
    public ApiResult exceptionHandler(HttpServletRequest request, ValidException exception) {
        String errMsg = exception.getMessage();
        log.error("url:[{}] [{}]", request.getRequestURI(), errMsg);
        return ApiResult.failed(errMsg);
    }

    /**
     * 处理TmallException异常
     *
     * @param request
     * @param exception
     * @return
     */
    @ExceptionHandler(TmallException.class)
    public ApiResult<String> exceptionHandler(HttpServletRequest request, TmallException exception) {
        log.error("url:[{}] [{}]", request.getRequestURI(), exception.getError());
        return ApiResult.failed(exception.getResultCode(), exception.getError());
    }

    /**
     * 捕获方法参数校验异常
     *
     * @param exception
     * @return
     */
    @ExceptionHandler(ConstraintViolationException.class)
    public ApiResult<Set<ResponseAlertMessage>> exceptionHandler(HttpServletRequest request, ConstraintViolationException exception) {
        Set<ConstraintViolation<?>> message = exception.getConstraintViolations();
        Set<ResponseAlertMessage> msgSet = message.stream().map(msg -> ResponseAlertMessage.build(msg.getMessage())).collect(Collectors.toSet());
        log.error("url:[{}] [{}]", request.getRequestURI(), exception.getMessage());
        return ApiResult.failed(ResultCode.PARAM_ERROR, msgSet);
    }

    /**
     * 捕获方法参数校验异常
     *
     * @param request
     * @param exception
     * @return
     */
    @ExceptionHandler(MissingServletRequestParameterException.class)
    public ApiResult<String> exceptionHandler(HttpServletRequest request, MissingServletRequestParameterException exception) {
        String errMsg = exception.getParameterName() + "不能为空";
        log.error("url:[{}] [{}]", request.getRequestURI(), errMsg);
        return ApiResult.failed(ResultCode.PARAM_ERROR, errMsg);
    }

    /**
     * 捕获方法参数校验异常
     *
     * @param request
     * @param exception
     * @return
     */
    @ExceptionHandler(MissingServletRequestPartException.class)
    public ApiResult<String> exceptionHandler(HttpServletRequest request, MissingServletRequestPartException exception) {
        String errMsg = exception.getRequestPartName() + "不能为空";
        log.error("url:[{}] [{}]", request.getRequestURI(), errMsg);
        return ApiResult.failed(ResultCode.PARAM_ERROR, errMsg);
    }

    /**
     * 捕获实体参数校验异常
     *
     * @param request
     * @param exception
     * @return
     */
    @ExceptionHandler(MethodArgumentNotValidException.class)
    public ApiResult<String> exceptionHandler(HttpServletRequest request, MethodArgumentNotValidException exception) {
        BindingResult bindingResult = exception.getBindingResult();
        Objects.requireNonNull(bindingResult);
        String errMsg = null;
        if (bindingResult.hasErrors()) {
            errMsg = bindingResult.getFieldErrors().stream()
                    .map(DefaultMessageSourceResolvable::getDefaultMessage)
                    .collect(Collectors.joining(";"));
        }
        log.error("url:[{}] [{}]", request.getRequestURI(), errMsg);
        return ApiResult.failed(ResultCode.PARAM_ERROR, errMsg);
    }

    /**
     * 捕获请求方式不支持异常
     *
     * @param request
     * @param exception
     * @return
     */
    @ExceptionHandler(HttpRequestMethodNotSupportedException.class)
    public ApiResult<String> exceptionHandler(HttpServletRequest request, HttpRequestMethodNotSupportedException exception) {
        String errMsg = "请求方式不支持";
        log.error("url:[{}] [{}]", request.getRequestURI(), errMsg);
        return ApiResult.failed(ResultCode.REQUEST_METHOD_NOT_ALLOWED, errMsg);
    }

    /**
     * 捕获不合法参数异常
     *
     * @param request
     * @param exception
     * @return
     */
    @ExceptionHandler(IllegalArgumentException.class)
    public ApiResult<String> exceptionHandler(HttpServletRequest request, IllegalArgumentException exception) {
        String errMsg = exception.getMessage();
        log.error("url:[{}] [{}]", request.getRequestURI(), errMsg);
        return ApiResult.failed(ResultCode.PARAM_ERROR, errMsg);
    }

    /**
     * 捕获请求参数校验失败异常
     *
     * @param request
     * @param exception
     * @return
     */
    @ExceptionHandler(BindException.class)
    public ApiResult<String> exceptionHandler(HttpServletRequest request, BindException exception) {
        BindingResult bindingResult = exception.getBindingResult();
        Objects.requireNonNull(bindingResult);
        String errMsg = bindingResult.getFieldError().getDefaultMessage();
        log.error("url:[{}] [{}]", request.getRequestURI(), errMsg);
        return ApiResult.failed(ResultCode.PARAM_ERROR, errMsg);
    }

    /**
     * 捕获请求参数体缺失异常
     *
     * @param request
     * @param exception
     * @return
     */
    @ExceptionHandler(HttpMessageNotReadableException.class)
    public ApiResult<String> exceptionHandler(HttpServletRequest request, HttpMessageNotReadableException exception) {
        String errMsg = "请求参数体不能为空";
        log.error("url:[{}] [{}]", request.getRequestURI(), errMsg);
        return ApiResult.failed(ResultCode.PARAM_ERROR, errMsg);
    }

    /**
     * 捕获上传文件大小受限异常
     *
     * @param request
     * @param exception
     * @return
     */
    @ExceptionHandler(MaxUploadSizeExceededException.class)
    public ApiResult<String> exceptionHandler(HttpServletRequest request, MaxUploadSizeExceededException exception) {
        String errMsg = String.format("上传文件大小受限，最大：%sM", exception.getMaxUploadSize() / 1024 / 1024);
        log.error("url:[{}] [{}]", request.getRequestURI(), errMsg);
        return ApiResult.failed(ResultCode.PARAM_ERROR, errMsg);
    }

    /**
     * 处理任何 Exception 接口实现类异常
     *
     * @param request
     * @param exception
     * @return
     */
    @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
    @ExceptionHandler(Exception.class)
    public ApiResult<String> exceptionHandler(HttpServletRequest request, Exception exception) {
        log.error("url:[{}] [{}]", request.getRequestURI(), exception.getMessage(), exception);
        return ApiResult.failed(exception.getMessage());
    }

}